/* ====================================================================
 * (#)Header: RuleEngineJsr94.java
 * (#)Revision: 
 * (#)Date Created: Sep 30, 2011 12:47:15 AM
 * (#)Created By: Karol Wozniak
 * ====================================================================
 *
 * Copyright (c) 2007 karolak .  All rights reserved.
 *
 * ====================================================================
 * 
 * The software application represented here is the sole property of 
 * karolak. Its unauthorized utilization is expressly prohibited.
 * 
 * ====================================================================
 */
package com.karolak.rules.jsr94;

import java.io.Reader;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.rules.RuleRuntime;
import javax.rules.RuleServiceProvider;
import javax.rules.RuleServiceProviderManager;
import javax.rules.StatelessRuleSession;
import javax.rules.admin.LocalRuleExecutionSetProvider;
import javax.rules.admin.RuleAdministrator;
import javax.rules.admin.RuleExecutionSet;

import com.karolak.rules.RuleEngine;
import com.karolak.rules.RuleFileType;
import com.karolak.rules.RuleReaderProvider;

/**
 * <code>RuleEngineJsr94</code> is used to ....
 * 
 * @author <a href="mailto:kwozniak@hotmail.com">Karol Wozniak</a>
 */
public class RuleEngineJsr94 implements RuleEngine {

	private String ruleServiceProviderClassName;
	private String ruleServiceProviderUri;
	private RuleReaderProvider ruleReaderProvider;

	private RuleServiceProvider ruleServiceProvider;
	private RuleAdministrator ruleAdministrator;
	private LocalRuleExecutionSetProvider ruleExecutionSetProvider;
	private RuleRuntime ruleRuntime;

	public RuleEngineJsr94(String ruleServiceProviderClassName, String ruleServiceProviderUri, RuleReaderProvider ruleReaderProvider) {
		setRuleServiceProviderClassName(ruleServiceProviderClassName);
		setRuleServiceProviderUri(ruleServiceProviderUri);
		setRuleReaderProvider(ruleReaderProvider);
		initialize();
	}

	// === public rule api === //
	public String loadRuleSet(StringBuilder ruleString, RuleFileType fileType) {
		Reader reader = getRuleReaderProvider().create(ruleString, fileType);
		Map<?, ?> map = getRuleReaderProvider().createProperties();
		String uri = null;
		try {
			RuleExecutionSet ruleExecutionSet = getRuleExecutionSetProvider().createRuleExecutionSet(reader, map);
			uri = ruleExecutionSet.getName();
			getRuleAdministrator().registerRuleExecutionSet(uri, ruleExecutionSet, null);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return uri;
	}

	public String loadRuleSet(String fileName, RuleFileType fileType) {
		Reader reader = getRuleReaderProvider().create(fileName, fileType);
		Map<?, ?> map = getRuleReaderProvider().createProperties();
		String uri = null;
		try {
			RuleExecutionSet ruleExecutionSet = getRuleExecutionSetProvider().createRuleExecutionSet(reader, map);
			uri = ruleExecutionSet.getName();
			getRuleAdministrator().registerRuleExecutionSet(uri, ruleExecutionSet, null);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return uri;
	}

	public List<?> execute(String ruleExecutionSetUri, Object... args) {
		List<?> data = Arrays.asList(args);
		List<?> result = null;
		try {
			StatelessRuleSession session = (StatelessRuleSession) getRuleRuntime().createRuleSession(ruleExecutionSetUri, null, RuleRuntime.STATELESS_SESSION_TYPE);
			result = session.executeRules(data);
			session.release();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	public List<?> execute(String ruleExecutionSetUri, List<?> data) {
		List<?> result = null;
		try {
			StatelessRuleSession session = (StatelessRuleSession) getRuleRuntime().createRuleSession(ruleExecutionSetUri, null, RuleRuntime.STATELESS_SESSION_TYPE);
			result = session.executeRules(data);
			session.release();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	// === protected api === //
	protected void initialize() {
		try {
			Class.forName(getRuleServiceProviderClassName());
			RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(getRuleServiceProviderUri());
			RuleAdministrator ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
			LocalRuleExecutionSetProvider ruleExecutionSetProvider = ruleAdministrator.getLocalRuleExecutionSetProvider(null);
			RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();

			setRuleServiceProvider(ruleServiceProvider);
			setRuleAdministrator(ruleAdministrator);
			setRuleExecutionSetProvider(ruleExecutionSetProvider);
			setRuleRuntime(ruleRuntime);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// === get/set === //
	public RuleReaderProvider getRuleReaderProvider() {
		return ruleReaderProvider;
	}

	public void setRuleReaderProvider(RuleReaderProvider ruleReaderProvider) {
		this.ruleReaderProvider = ruleReaderProvider;
	}

	public String getRuleServiceProviderClassName() {
		return ruleServiceProviderClassName;
	}

	public void setRuleServiceProviderClassName(String ruleServiceProviderClassName) {
		this.ruleServiceProviderClassName = ruleServiceProviderClassName;
	}

	public String getRuleServiceProviderUri() {
		return ruleServiceProviderUri;
	}

	public void setRuleServiceProviderUri(String ruleServiceProviderUri) {
		this.ruleServiceProviderUri = ruleServiceProviderUri;
	}

	// === jsr94 services === //
	public RuleServiceProvider getRuleServiceProvider() {
		return ruleServiceProvider;
	}

	public void setRuleServiceProvider(RuleServiceProvider ruleServiceProvider) {
		this.ruleServiceProvider = ruleServiceProvider;
	}

	public RuleAdministrator getRuleAdministrator() {
		return ruleAdministrator;
	}

	public void setRuleAdministrator(RuleAdministrator ruleAdministrator) {
		this.ruleAdministrator = ruleAdministrator;
	}

	public LocalRuleExecutionSetProvider getRuleExecutionSetProvider() {
		return ruleExecutionSetProvider;
	}

	public void setRuleExecutionSetProvider(LocalRuleExecutionSetProvider ruleExecutionSetProvider) {
		this.ruleExecutionSetProvider = ruleExecutionSetProvider;
	}

	public RuleRuntime getRuleRuntime() {
		return ruleRuntime;
	}

	public void setRuleRuntime(RuleRuntime ruleRuntime) {
		this.ruleRuntime = ruleRuntime;
	}

	@SuppressWarnings("unchecked")
	public List<String> getRuleExecutionSetUris() {
		try {
			return getRuleRuntime().getRegistrations();
		} catch (RemoteException e) {
			throw new RuntimeException(e);
		}
	}
}
